package com.hmsproject.db.inmemorydb;

import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

import com.hmsproject.common.HMSUtil;
import com.hmsproject.common.MailUtil;
import com.hmsproject.datastructures.HotelImage;
import com.hmsproject.datastructures.Hotel;
import com.hmsproject.datastructures.StarValue;
import com.hmsproject.datastructures.User;
import com.hmsproject.datastructures.comparators.HotelDistanceComparatorDescending;
import com.hmsproject.datastructures.comparators.HotelNameComparatorAscending;
import com.hmsproject.datastructures.comparators.HotelPopularityComparatorDescending;
import com.hmsproject.datastructures.comparators.HotelPriceComparatorAscending;
import com.hmsproject.datastructures.comparators.HotelPriceComparatorDescending;
import com.hmsproject.datastructures.comparators.HotelReviewComparatorDescending;
import com.hmsproject.datastructures.comparators.HotelStarRatingComparatorAscending;
import com.hmsproject.datastructures.comparators.HotelStarRatingComparatorDescending;
import com.hmsproject.datastructures.deal.Criterion;
import com.hmsproject.datastructures.deal.Deal;
import com.hmsproject.datastructures.deal.DealDetails;
import com.hmsproject.datastructures.enums.DealTypeEnum;
import com.hmsproject.datastructures.event.Event;
import com.hmsproject.datastructures.event.EventSchedule;
import com.hmsproject.datastructures.transactions.Transaction;
import com.hmsproject.datastructures.transactions.UserPreferences;
import com.hmsproject.db.HMSDBConnection;
import com.hmsproject.db.HMSDBConnectionFactory;
import com.hmsproject.db.HMSDBType;
import com.hmsproject.db.expediadb.HMSExpediaDBConstants;
import com.hmsproject.db.util.HMSDBUtil;
import com.hmsproject.livedata.LiveDealCriterion;
import com.hmsproject.livedata.LiveDealData;
import com.hmsproject.search.bean.HotelInfo;
import com.hmsproject.search.bean.HotelSorting;
import com.hmsproject.search.bean.PlaceBean;
import com.hmsproject.search.bean.SearchBean;
import com.mymobile.showcase.mobile.ForgotPasswordView;
import com.mymobile.showcase.mobile.SignInUserView;
import com.mymobile.showcase.mobile.SignUpUserView;

public class HMSInMemoryDBConnection extends HMSDBConnection {
	
	private static HMSInMemoryDBConnection hmsInMemoryDBConnection = new HMSInMemoryDBConnection();
	
	
	private HMSInMemoryDBConnection()
	{
		 //userMap = new HashMap<String,User>();
		 //userMap.put("hmsuser441@gmail.com", new User("Admin","User","hmsuser441@gmail.com","hmsuser123"));
	}

	public static HMSInMemoryDBConnection getInstance(){
		return hmsInMemoryDBConnection;
	}
	
	@Override
	public long signUpUser(SignUpUserView signUpUserView) throws Exception{
		
		
		if(UserMap.getInstance().getUser(signUpUserView.getEmailAddress()) != null){
			throw new Exception("User " + signUpUserView.getEmailAddress() + " already exists.");
		}
		
		long verificationCode = new Random().nextLong();
		
		User user = new User(signUpUserView.getFirstName(), signUpUserView.getLastName(), signUpUserView.getEmailAddress(), signUpUserView.getPassword());
		user.setVerificationCode(verificationCode);
		user.setVerified(false);
		
		UserMap.getInstance().addUser(user);
		//userMap.put(user.getEmailAddress(), user);
		
		return verificationCode;
	}
	
	@Override
	public boolean signInUser(SignInUserView signInUserView) {
		
		boolean loginSuccessful = false;
		
		User user = UserMap.getInstance().getUser(signInUserView.getEmailAddress());
		
		if(user != null && user.isVerified() && user.getPassword().equals(signInUserView.getPassword())){
			loginSuccessful = true;
		}
		
		return loginSuccessful;

	}	
	
	@Override
	public String forgotPassword(ForgotPasswordView forgotPasswordView) throws Exception{
		
		String password = null;
		
		User user = UserMap.getInstance().getUser(forgotPasswordView.getEmailAddress());
		
		if(user != null){
			password = user.getPassword();
		}else{
			throw new Exception("User " + forgotPasswordView.getEmailAddress() + " does not exist.");
		}
		
		return password;
	}
	
	@Override
	public void updateUser(User user) {
		
		try{
			User persistedUser = UserMap.getInstance().getUser(user.getEmailAddress());
			persistedUser.setFirstName(user.getFirstName());
			persistedUser.setLastName(user.getLastName());
			persistedUser.setMobilephone(user.getMobilephone());
			persistedUser.setDob(user.getDob());
			persistedUser.setGender(user.getGender());
			persistedUser.setCountry(user.getCountry());
			persistedUser.setAddress(user.getAddress());
			persistedUser.setPostcode(user.getPostcode());
			persistedUser.setCity(user.getCity());


			
		}catch(Exception e){
			e.printStackTrace();
		}
		// TODO Auto-generated method stub
		
	}	
	
	@Override
	public User getUser(String emailAddress)
	{
		
		User user = new User();
		
		User persistedUser = UserMap.getInstance().getUser(emailAddress);
		
		if(persistedUser != null){
			user.setFirstName(persistedUser.getFirstName());
			user.setLastName(persistedUser.getLastName());
			user.setEmailAddress(persistedUser.getEmailAddress());
			user.setGender(persistedUser.getGender());
			user.setAddress(persistedUser.getAddress());
			user.setCity(persistedUser.getCity());
			user.setCountry(persistedUser.getCountry());
			user.setDob(persistedUser.getDob());
			user.setPostcode(persistedUser.getPostcode());
			user.setMobilephone(persistedUser.getMobilephone());
		}
		
		return user;
	}		
	
	@Override
	public boolean verifyUser(String verificationCode) throws Exception
	{
		boolean result = false;
		 
		Collection<User> userList = UserMap.getInstance().getAllUsers();
		
		Iterator<User> iterator = userList.iterator();

		while(iterator.hasNext()){
			User user = iterator.next();
			
			if((user.getVerificationCode()+"").equals(verificationCode)){
				user.setVerified(true);
				result= true;
				break;
			}
		}
		
		return result;
	}	
	
	@Override
	public boolean changePassword(String emailAddress,String newPassword)
	{
		boolean result = false;
		
		
		User user = UserMap.getInstance().getUser(emailAddress);

		user.setPassword(newPassword);
		
		result = true;
		
		return result;
	}		
	
	@Override
	public Deal getDeal(String dealId){
		return DealMap.INSTANCE.getDeal(dealId);		
	}
	
	@Override
	public Deal getDeal(String hotelid, Date checkinDate, Date checkoutDate){
		return DealMap.INSTANCE.getDeal(hotelid, checkinDate, checkoutDate);		
	}		
	
	@Override
	public List<Deal> getDeals(DealTypeEnum dealTypeEnum, String hotelid){
		
		return DealMap.INSTANCE.getDeals(dealTypeEnum, hotelid);		
	}	
	
	@Override
	public Transaction getTransaction(String userId,String dealId){
		return TransactionMap.INSTANCE.getTransaction(userId, dealId);		
	}	
	
	@Override
	public List<Transaction> getTransactionList(String userId){
		return TransactionMap.INSTANCE.getTransactionList(userId);			
	}	
	
	@Override
	public Transaction buyDeal(String userId, String dealId, UserPreferences userPreferences,Date transactionDate, int noOfRoomsBooked,LiveDealData liveDealData){
		Transaction transaction = new Transaction(userId, DealMap.INSTANCE.getDeal(dealId), userPreferences,transactionDate,0);
		TransactionMap.INSTANCE.addTransaction(transaction);
		return transaction;		
	}
	
	@Override
	public Event createEvent (String eventName, String eventLocation,EventSchedule eventSchedule){
		Event event = new Event(eventName, eventLocation, eventSchedule);
		EventMap.getInstance().addEvent(event);
		return event;		
	}
	
	@Override
	public Event createEvent (Event event){
		EventMap.getInstance().addEvent(event);
		return event;		
	}		
	
	@Override
	public Deal createDeal(String dealName,String hotelId,Date cutOff,DealDetails dealDetails, List<Event> events){
		Deal deal = new Deal(dealName, hotelId, dealDetails,events);
		DealMap.INSTANCE.addDeal(deal);
		return deal;	
	}
	
	@Override
	public Deal createDeal(Deal deal){
		DealMap.INSTANCE.addDeal(deal);
		return deal;	
	}		
	
	@Override
	public List<Deal> getDealList(String eventId){
		return DealMap.INSTANCE.getDealList(eventId);		
	}
	
	public String getHotelIds(Date checkinDate,Date checkoutDate,String location)
	{
		return DealMap.INSTANCE.fetchHotels(checkinDate, checkoutDate, location);
	}	
	
	@Override
	public List<Hotel> fetchHotels(boolean isDealPresent,Date checkinDate,Date checkoutDate,String location)
	{
		List<Hotel> hotelListReturn = null;
		if(isDealPresent){
			String hotelIdList = DealMap.INSTANCE.fetchHotels(checkinDate, checkoutDate, location);
			hotelListReturn = HMSDBConnectionFactory.getInstance().getDBConnection(HMSDBType.EXPEDIADB).searchHotels(hotelIdList);
		}else
		{
			SearchBean searchBean = new SearchBean();
			PlaceBean placeBean = new PlaceBean();
			placeBean.setPlaceName(location);
			placeBean.setFromDate(checkinDate);
			placeBean.setToDate(checkoutDate);
			searchBean.setPlaceBean(placeBean);
			hotelListReturn = HMSDBConnectionFactory.getInstance().getDBConnection(HMSDBType.EXPEDIADB).searchHotels(searchBean);
		}
		
		return hotelListReturn;
	}	
	
	@Override
	public LiveDealData getLiveDealData(String dealId){
		
		if(dealId.equals("12345")){
			return getDummyLiveDealData();
		}
		
		LiveDealData liveDealData = new LiveDealData();
		Deal deal = DealMap.INSTANCE.getDeal(dealId);
		
		if(deal.isActive()){
			
			Event event = deal.getEvents().get(0);
			liveDealData.setFromDate(event.getEventSchedule().getFromDate());
			liveDealData.setToDate(event.getEventSchedule().getToDate());
			
			liveDealData.setTotalDays((int)((liveDealData.getToDate().getTime() - liveDealData.getFromDate().getTime()) / (1000 * 60 * 60 * 24)));
			
			liveDealData.setDeal(deal);
			
		    Date d2 = deal.getDealDetails().getCutOff();
		    Date d1 = new Date();
			int noOfDaysLeft = (int)(((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24)));

			liveDealData.setNoOfDaysLeft(noOfDaysLeft);
			
			String hotelId = deal.getHotelId();
			List<Hotel> hotels = HMSDBConnectionFactory.getInstance().getDBConnection(HMSDBType.INMEMORYDB).searchHotels(hotelId);
			Hotel hotel = hotels.get(0);
			liveDealData.setHotel(hotel);
			
			int totalAlreadyBooked = TransactionMap.INSTANCE.getTransactionRoombookedForDeal(dealId);
			
			liveDealData.setAlreadyBooked(totalAlreadyBooked);
			liveDealData.setAvailableForBooking(liveDealData.getAvailableForBooking() - totalAlreadyBooked);
			
			/*** setting live criterions ***/
			List<Criterion> dealCriterions = deal.getDealDetails().getCriterions();
			List<LiveDealCriterion> liveDealCriterions = new ArrayList<LiveDealCriterion>();
			int currentBookingCount = 0;
			boolean dealThru = false;
			boolean midDealThru = false;
			int lastDealpercent = 0;
			for(int i = 0; i < dealCriterions.size(); i++){
				
				if(!dealThru && totalAlreadyBooked >= (currentBookingCount + dealCriterions.get(i).getNoOfRooms())){
					currentBookingCount += dealCriterions.get(i).getNoOfRooms();
					lastDealpercent += dealCriterions.get(i).getAdditionalPercentOff();
					continue;
				}
				
				dealThru = true;
				
				int noOfRooms = dealCriterions.get(i).getNoOfRooms();
				if(!midDealThru){
					noOfRooms = (noOfRooms + currentBookingCount) - totalAlreadyBooked;
					midDealThru = true;
				}
				
				LiveDealCriterion liveDealCriterion = new LiveDealCriterion(dealCriterions.get(i).getAdditionalPercentOff(), noOfRooms);
				liveDealCriterions.add(liveDealCriterion);
				currentBookingCount += dealCriterions.get(i).getNoOfRooms();
			}
			liveDealData.setLiveDealCriterions(liveDealCriterions);
			liveDealData.setAfterDiscount(lastDealpercent);
			/****/
			
			int originalPrice = hotel.getPrice();
			liveDealData.setCurrentPrice(originalPrice - originalPrice*lastDealpercent/100);
			liveDealData.setOriginalPrice(originalPrice);
			liveDealData.setDealUrl("http://" + HMSUtil.getInstance().getServerAddress() + ":" + HMSDBUtil.PORT+"/hms/deal/viewHotel.xhtml?dealId="+ dealId);
								
			
		}

		return liveDealData;
	}
	
    @Override
	public List<Transaction> getTransactionListForDeal(String dealId){
		return TransactionMap.INSTANCE.getTransactionListForDeal(dealId);
	}	
    
    private LiveDealData getDummyLiveDealData(){
    	LiveDealData liveDealData = new LiveDealData();
    	
    	Deal deal = new Deal("XMAS offer",  null, null, null);
    	
    	liveDealData.setNoOfDaysLeft(10);
    	liveDealData.setDeal(deal);
    	
		List<Hotel> hotels = HMSDBConnectionFactory.getInstance().getDBConnection(HMSDBType.EXPEDIADB).searchHotels("115718");
		Hotel hotel = hotels.get(0);
		liveDealData.setHotel(hotel);    	
    	
		liveDealData.setAlreadyBooked(5);
		
		List<LiveDealCriterion> liveDealCriterions = new ArrayList<LiveDealCriterion>();
		LiveDealCriterion liveDealCriterion1 = new LiveDealCriterion(5, 7);
		LiveDealCriterion liveDealCriterion2 = new LiveDealCriterion(10, 10);
		liveDealCriterions.add(liveDealCriterion1);
		liveDealCriterions.add(liveDealCriterion2);
		liveDealData.setLiveDealCriterions(liveDealCriterions);
		
		liveDealData.setCurrentPrice(200);
		
    	return liveDealData;
    }
    
    
	@Override
	public List<Hotel> searchHotels(String hotelIdList)
	{
		return HotelMap.getInstance().getHotels(hotelIdList);
	}	    
	
	@Override
	public List<Hotel> searchHotels(SearchBean searchBean)
	{
		
		if(searchBean.getFilterBean().isDealOnly()){
			String hotelIdList = HMSDBConnectionFactory.getInstance().getDBConnection(HMSDBType.INMEMORYDB).getHotelIds(searchBean.getPlaceBean().getFromDate(), searchBean.getPlaceBean().getToDate(), searchBean.getPlaceBean().getPlaceName());
			
			return searchHotels(hotelIdList);
		}		
		
		List<Hotel> hotelList = HotelMap.getInstance().getAllHotels(searchBean);
		setSearchBeanProcessing(searchBean, hotelList);
		
		return hotelList;
	}
	
	
	@Override
	public List<HotelInfo> searchHotelsExpedia(SearchBean searchBean)
	{
		int[] dealMapperInex = {1,4,7,9};
		List<Hotel> hotelList = HMSDBConnectionFactory.getInstance().getDBConnection(HMSDBType.EXPEDIADB).searchHotels(searchBean);
		
		List<HotelInfo> hotelInfoList = new ArrayList<>();
		
		if(hotelList.isEmpty()){
			return hotelInfoList;
		}
		
		String eventId = searchBean.getEventId();
		
		List<Deal> deals = null;
		if(eventId != null && !eventId.isEmpty()){
		   deals = new ArrayList<Deal>(DealMap.INSTANCE.getDealsBasedOnEventId(eventId));	
		}else{		
		   deals= new ArrayList<Deal>(DealMap.INSTANCE.getDealMapper().values());
		}   
		
		   for(Hotel hotel:hotelList){
			   hotelInfoList.add(new HotelInfo(hotel));
		   }
		
		   for (int i = 0; i < dealMapperInex.length; i++) {
			       Hotel hotel = hotelList.get(dealMapperInex[i]);
			       hotel.setHasDeal(true);
				   deals.get(i).setHotelId(hotel.getHotelId());
		   }		
		HotelMap.getInstance().setHotelMapper(hotelList);
		return hotelInfoList;
	}	
	
	
	private void setSearchBeanProcessing(SearchBean searchBean,List<Hotel> hotelList){
		
		processPlaceBean(searchBean.getPlaceBean(), hotelList);
		processHotelSorting(searchBean.getHotelSorting(), hotelList);		
		
	}
	
	private void processHotelSorting(HotelSorting hotelSorting,List<Hotel> hotelList){
		
		if(hotelSorting == null){
			return;
		}
		
		if(hotelSorting == HotelSorting.NAME_A_TO_Z){
			Collections.sort(hotelList, HotelNameComparatorAscending.getInstance());
		}else
		if(hotelSorting == HotelSorting.PRICE_LOW_TO_HIGH){
			Collections.sort(hotelList, HotelPriceComparatorAscending.getInstance());
		}else
		if(hotelSorting == HotelSorting.PRICE_HIGH_TO_LOW){
			Collections.sort(hotelList, HotelPriceComparatorDescending.getInstance());
		}else
		if(hotelSorting == HotelSorting.STAR_ZERO_TO_FIVE){
			Collections.sort(hotelList, HotelStarRatingComparatorAscending.getInstance());
		}else
		if(hotelSorting == HotelSorting.STAR_FIVE_ZERO){
			Collections.sort(hotelList, HotelStarRatingComparatorDescending.getInstance());
		}else
		if(hotelSorting == HotelSorting.POPULARITY){
			Collections.sort(hotelList, HotelPopularityComparatorDescending.getInstance());
		}else
		if(hotelSorting == HotelSorting.DISTANCE){
			Collections.sort(hotelList, HotelDistanceComparatorDescending.getInstance());
		}else
		if(hotelSorting == HotelSorting.REVIEW_SCORE){
			Collections.sort(hotelList, HotelReviewComparatorDescending.getInstance());
		}
		
		
	}
	
	private void processPlaceBean(PlaceBean placeBean,List<Hotel> hotelList){

		/*if(placeBean == null){
			return;
		}
		
		if(placeBean.getPlaceName() != null){
			filterquery.append(Hotels_DB_enum.PLACE_NAME.getValue(),placeBean.getPlaceName());
		}*/

	}	
    
    public static void main(String args[]){
    	//System.out.println(HMSInMemoryDBConnection.getInstance().getDummyLiveDealData().getHotel().getName());
    	
    	//HMSInMemoryDBConnection.getInstance().getLiveDealData("1");
    	
    	Date d1 = new Date(2014, 12, 5);
    	Date d2 = new Date(2015, 1, 25);
    	
    	System.out.println("NO OF DAYS:   " + (int)((d2.getTime() - d1.getTime()) / (1000 * 60 * 60 * 24)));
    }
	
}
